Java集合(13)--LinkedHashMap源码分析
HashMap使用哈希表来存储数据,并用拉链法来处理冲突。LinkedHashMap继承自HashMap,同时自身有一个链表,使用链表存储数据,不存在冲突。
LinkedList和LinkedHashMap一样使用一个双向循环链表,但LinkedList存储的是简单的数据,并不是“键值对”。
LinkedList和LinkedHashMap都可以维护内容的顺序,但HashMap不维护顺序。
public class LinkedHashMap<K,V> extends HashMap<K,V> implements Map<K,V>
HashMap的init方法没有实现,但是LinkedHaqshMap已经对其实现:
void init() { //初始化一个Entry类型的header header = new Entry<K,V>(-1, null, null, null); header.before = header.after = header; }
在LinkedHashMap中多了一个accessOrder变量,他表示迭代时候的一个顺序,若为true,则按照读取顺序排序(读得越多在链表的越后面,读得越少在链表的越前面,LRU,最近最少使用),若为false则按照插入顺序排序.从LinkedHaqshMap的前4个构造方法可以看出,accessOrder默认为false,故按照插入顺序进行排序。
void recordAccess(HashMap<K,V> m) { LinkedHashMap<K,V> lm = (LinkedHashMap<K,V>)m; if (lm.accessOrder) { lm.modCount++; remove(); addBefore(lm.header); } }
在LRU算法中,最少使用的页面被先换出,最近使用的很可能以后还会使用。
他判断accessOrder属性,若为true,则执行一个叫做LRU的算法,将刚访问的entry移除,然后加到header前面,这样迭代的时候会优先迭代最近频繁访问的entry(不是链表头),从而就改变了迭代的顺序。
void addEntry(int hash, K key, V value, int bucketIndex) { createEntry(hash, key, value, bucketIndex); Entry<K,V> eldest = header.after; if (removeEldestEntry(eldest)) { removeEntryForKey(eldest.key); } else { if (size >= threshold) resize(2 * table.length); } }
protected boolean removeEldestEntry(Map.Entry<K,V> eldest) { //始终返回的是false
return false; }
启发:若希望将Map当做Cache来使用,并且限制大小,只需继承LinkedHashMap并重写removeEldestEntry(Entry<K,V> eldest)方法,像这样:
private static final int MAX_ENTRIES = 100; protected boolean removeEldestEntry(Map.Entry eldest) { return size() > MAX_ENTRIES; }
实现最近被使用(LRU)缓存:
import java.util.LinkedHashMap; import java.util.Map; public LRUCache<K, V> extends LinkedHashMap<K, V> { private int cacheSize; public LRUCache(int cacheSize) { super(16, 0.75, true);//排序策略 this.cacheSize = cacheSize; }